package com.codrim.cs.data.server.subservice;

import com.codrim.common.utils.exception.Assert;
import com.codrim.cs.data.rpc.dto.param.WholeDayDataParamDTO;
import com.codrim.cs.data.rpc.dto.result.WholeDayDataResultDTO;
import com.codrim.cs.data.server.dao.entity.DailyDataRecordDO;
import com.codrim.cs.data.server.dao.entity.StatCsAdvAllDO;
import com.codrim.cs.data.server.dao.entity.StatCsAffAllDO;
import com.codrim.cs.data.server.dao.param.DataRecordParam;
import com.codrim.cs.data.server.dao.param.DayStatCsAdvAllParam;
import com.codrim.cs.data.server.utils.DateUtils;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.assertj.core.util.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiFunction;

import javax.annotation.Resource;

import static org.apache.commons.collections.CollectionUtils.isNotEmpty;

/**
 * Created by Liang.Zhuge on 28/07/2017.
 */
@Service
public class DataSubService implements SubService {
    private static final Logger logger = LoggerFactory.getLogger(DataSubService.class);

    @Resource TimeZoneSubService timeZoneSubService;

    /**
     * <p>获取广告主一整天的汇总数据的处理.</p>
     * <p>代码大致的逻辑如下: <ul> <li>1. 校验参数</li> <li>2. 查询一整天的统计结果</li> <li>3. 查询可能的手工输入的数据</li> <li>4. 使用2和3两部分数据,并返回</li>
     * </ul> </p>
     *
     * @param param              查询参数
     * @param dailyStatConsumer  获取统计数据的函数
     * @param dataResultConsumer 获取手工输入统计数据的函数
     * @return 天汇总数据
     */
    public List<WholeDayDataResultDTO> wholeDayDataForAdv(
            WholeDayDataParamDTO param,
            BiFunction<DayStatCsAdvAllParam, String, List<StatCsAdvAllDO>> dailyStatConsumer,
            BiFunction<DataRecordParam, String, List<DailyDataRecordDO>> dataResultConsumer) {
        logger.info("Whole day data, param:{}", param);
        Assert.notNull(param, "Invalid param, can not be null");
        final Date date = param.getDate();
        final Integer companyId = param.getCompanyId();
        final String zone = timeZoneSubService.prefixTableNameForTimeZone(param.getZone());

        Assert.notNull(date, "Invalid param, date field can not be null");
        Assert.notNull(companyId, "Invalid param, companyId field can not be null");
        Assert.notNull(zone, "Invalid param, sZone can not be null");

        final String pattern = "yyyyMMdd";
        final String sUtcDate = DateUtils.toUTCString(date, pattern);
        logger.info("Advertiser whole day data, utc date:{}, default date:{}, company id:{}, sZone:{}",
                sUtcDate, DateUtils.toString(date, pattern), companyId, zone);

        //拼接权限参数，查询出统计数据
        DayStatCsAdvAllParam dayStatCsAdvAllParam = new DayStatCsAdvAllParam();
        dayStatCsAdvAllParam.setCompanyId(companyId);
        dayStatCsAdvAllParam.setSdate(NumberUtils.toInt(sUtcDate));
        dayStatCsAdvAllParam.setAffiliateManagerIdListSelected(param.getAffiliateManagerIdListSelected());
        dayStatCsAdvAllParam.setAdvertiserManagerIdListSelected(param.getAdvertiserManagerIdListSelected());
        Optional.ofNullable(param.getAdvertiserManagerId()).ifPresent(dayStatCsAdvAllParam::setAdvertiserManagerId);
        Optional.ofNullable(param.getAffiliateManagerId()).ifPresent(dayStatCsAdvAllParam::setAffiliateManagerId);
        dayStatCsAdvAllParam.setHasAllPermission(param.getHasAllPermission());
        final Set<Integer> advertiserManagerDepartmentIdSet = param.getAdvertiserManagerDepartmentIdSet();
        final Set<Integer> affiliateManagerDepartmentIdSet = param.getAffiliateManagerDepartmentIdSet();
        if(isNotEmpty(advertiserManagerDepartmentIdSet)){
            dayStatCsAdvAllParam.setAdvertiserManagerDepartmentIdSet(advertiserManagerDepartmentIdSet);
        }
        if(isNotEmpty(affiliateManagerDepartmentIdSet)){
            dayStatCsAdvAllParam.setAffiliateManagerDepartmentIdSet(affiliateManagerDepartmentIdSet);
        }
        final List<StatCsAdvAllDO> data = dailyStatConsumer.apply(dayStatCsAdvAllParam, zone);

        //拼接权限参数，查询出录入数据
        DataRecordParam dataRecordParam = new DataRecordParam();
        dataRecordParam.setCompanyId(param.getCompanyId());
        dataRecordParam.setAffiliateManagerIdListSelected(param.getAffiliateManagerIdListSelected());
        dataRecordParam.setAdvertiserManagerIdListSelected(param.getAdvertiserManagerIdListSelected());
        Optional.ofNullable(param.getAdvertiserManagerId()).ifPresent(dataRecordParam::setAdvertiserManagerId);
        Optional.ofNullable(param.getAffiliateManagerId()).ifPresent(dataRecordParam::setAffiliateManagerId);
        dataRecordParam.setHasAllPermission(param.getHasAllPermission());
        if(isNotEmpty(advertiserManagerDepartmentIdSet)){
            dataRecordParam.setAdvertiserManagerDepartmentIdSet(advertiserManagerDepartmentIdSet);
        }
        if(isNotEmpty(affiliateManagerDepartmentIdSet)){
            dataRecordParam.setAffiliateManagerDepartmentIdSet(affiliateManagerDepartmentIdSet);
        }
        List<DailyDataRecordDO> dataRecords = dataResultConsumer.apply(dataRecordParam , DateUtils.format(param.getDate(), "yyyy-MM-dd"));

        boolean noData = data == null || data.isEmpty();
        if (noData && dataRecords == null) {
            return null;
        }
        List<WholeDayDataResultDTO> result = Lists.newArrayList();
        if(!noData){
            data.forEach(obj -> result.add(obj.toWholeDayDataResultDTO()));
        }
        dataRecordToWoleDayDataResult(result, dataRecords);
        return result;
    }

    /**
     * <p>获取渠道一整天的汇总数据的处理.</p>
     * <p>代码大致的逻辑如下: <ul> <li>1. 校验参数</li> <li>2. 查询一整天的统计结果</li> <li>3. 查询可能的手工输入的数据</li> <li>4. 使用2和3两部分数据,并返回</li>
     * </ul> </p>
     *
     * @param data  统计数据
     * @param dataRecords 手工输入统计数据
     * @return 天汇总数据
     */
    public List<WholeDayDataResultDTO> wholeDayDataForAff(List<StatCsAffAllDO> data,
            List<DailyDataRecordDO> dataRecords) {

        boolean noData = data == null || data.isEmpty();

        if (noData && dataRecords == null) {
            return null;
        }
        List<WholeDayDataResultDTO> result = Lists.newArrayList();
        if(!noData){
            data.forEach(obj -> result.add(obj.toWholeDayDataResultDTO()));
        }
        dataRecordToWoleDayDataResult(result, dataRecords);
        return result;
    }

    private void dataRecordToWoleDayDataResult(List<WholeDayDataResultDTO> result,
        List<DailyDataRecordDO> dataRecords) {
        boolean noDataRecord = dataRecords == null;
        if (!noDataRecord) {
            dataRecords.forEach(record -> {
                if (record != null && StringUtils.isNotBlank(record.getSymbol())) {
                    result.add(record.toWholeDayDataResultDTO());
                } else {
                    logger.warn("dataRecord is null or dataRecord.symbol is blank. dataRecord:{}", record);
                }
            });
        }
    }
}
